package com.apache.zuul.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.apache.exception.BusinessException;
import com.apache.passport.common.PassportHelper;
import com.apache.passport.common.XmlWhiteUtils;
import com.apache.tools.ConfigUtil;
import com.apache.tools.DateUtils;
import com.apache.tools.StrUtil;
import com.apache.zuul.config.FilterConfiguration;
import com.google.common.collect.Maps;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URL;
import java.util.*;
import java.util.concurrent.atomic.LongAdder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * description: 限流过滤器
 *
 * @author dyh 创建时间：2018年03月19日
 */
@Component
public class GlobalZuulFilter extends ReqPermissionFilter {

    protected static Map<String, Map<String, LongAdder>> hystrix = Maps.newConcurrentMap();

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // 这边的order一定要大于org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter的order
        // 也就是要大于5
        // 否则，RequestContext.getCurrentContext()里拿不到serviceId等数据。
        //return Ordered.LOWEST_PRECEDENCE;
        return 9;
    }

    @Override
    public boolean shouldFilter() {
        // 这里可以考虑弄个限流开启的开关，开启限流返回true，关闭限流返回false
        return true;
    }

    @Override
    public Object run() {
        try {
            RequestContext currentContext = RequestContext.getCurrentContext();
            HttpServletResponse response = currentContext.getResponse();
            HttpServletRequest request = currentContext.getRequest();
            String isOpenSecurity = "false";//StrUtil.doNull(ConfigUtil.getInstance().getValueByKey("is_open_security_filter"),"true");
            String uri = request.getRequestURI();//获取uri信息
            String suffix = parseUrlSuffix(uri).toLowerCase();//获取后缀名
            if (StrUtil.isNotNull(suffix)) {//如果存在后缀为图片,css等格式,直接跳过,不拦截
                suffix = suffix.toLowerCase();//后缀名小写
                if (XmlWhiteUtils.SUFFIX.contains(suffix)) {
                    return null;
                }
            }

            if(uri.indexOf("/uniteAuth/randCode") !=-1 || uri.indexOf("/images/") !=-1 || uri.indexOf("/js/") !=-1
            || uri.startsWith("/fallback") || uri.startsWith("/error") || uri.startsWith("/common/") || uri.startsWith("/serverList") || uri.startsWith("/toLine")){
                return null;
            }
            //以下黑白名单处理逻辑
            String ip = getIpAddress(request,false);
            JSONObject zuulBlack = routeLocator.getRoute("black.white",ip);
            if("0".equals(zuulBlack.getString("infoType"))){//黑名单
                throw new BusinessException("系统拒绝访问，您的操作权限受限");
            }

            String key = String.valueOf(RequestContext.getCurrentContext().get("serviceId"));
            if (StringUtils.isEmpty(key) || "null".equalsIgnoreCase(key)) {
                URL routeHost = RequestContext.getCurrentContext().getRouteHost();
                if (routeHost != null) {
                    key = routeHost.toString();
                }
            }
            currentContext.addZuulRequestHeader("req-service-id", key);
            JSONObject route = routeLocator.getRoute("", key);
            if("1".equalsIgnoreCase(route.getString("ifHystrix"))){//开启了熔断
                long dd = Double.valueOf(StrUtil.doNull(route.getString("hstxCondition"), "1000")).longValue();
                String date = DateUtils.Now.fmt_yyyyMMdd();
                if (!hystrix.containsKey(uri)) {
                    Map<String, LongAdder> limit = new HashMap();
                    limit.put(date, new LongAdder());
                    hystrix.put(uri, limit);
                }
                Map<String, LongAdder> limit = hystrix.get(uri);
                boolean tryAcquire = true;
                if (limit.containsKey(date)) {
                    LongAdder lad = limit.get(date);
                    if (dd >= lad.sum()) {
                        lad.increment();
                        tryAcquire = false;
                    }
                } else {
                    limit.clear();
                    limit.put(date, new LongAdder());
                    tryAcquire = false;
                }
                if (tryAcquire) {//达到熔断条件
                    throw new BusinessException("该服务已达到服务请求最大上限，已暂停该服务");
                }
            }

            int count = doFilterInvoke(request,isOpenSecurity);
            if(count ==0){
                throw new BusinessException("系统拒绝访问，您的操作权限受限");
            }
            if(key.toLowerCase().indexOf("sso-server")!=-1){
                String tokenId = PassportHelper.getInstance().getCurrCookie(request);
                if(StrUtil.isNotNull(tokenId)){
                    String value = PassportHelper.getInstance().getCurrCookie(request, "_uc.sso");
                    currentContext.addZuulRequestHeader("zuulucsso", value);
                    currentContext.addZuulRequestHeader("zuultokenid", tokenId);
                }
            } else {
                String randCode = routeLocator.getCahceInfo(getIpAddress(request,true));
                if(StrUtil.isNotNull(randCode)) {
                    currentContext.addZuulRequestHeader("rand-code", randCode);
                }
                String goUrl = this.runPermission(key);
                if (StrUtil.isNotNull(goUrl)) {
                    currentContext.setSendZuulResponse(false);
                    response.sendRedirect(goUrl);
                }
            }
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
           // ReflectionUtils.rethrowRuntimeException(e);
        }
        return null;
    }

    private int doFilterInvoke(HttpServletRequest request,String isOpenSecurity) {
        String uri = request.getRequestURI();//获取uri信息
        logger.info("当前请求地址为：" + uri);
        /*if(!"true".equalsIgnoreCase(isOpenSecurity)){//没开启安全验证
            return 1;
        }*/
        if(uri.indexOf("/api/")==-1){
            return 1;
        }
        String referer = request.getHeader("Referer");
        String serverName = request.getServerName().toLowerCase();
        if("localhost".equals(serverName) || "127.0.0.1".equals(serverName)){
            return 1;
        }
        if(StrUtil.isNull(referer)){
            referer = request.getScheme()+"://"+getIpAddress(request,false);
            //request.getScheme()+"://"+request.getServerName();
        }
        String refererUrl = ConfigUtil.getInstance().getValueByKey("refererUrl");
        if(StrUtil.isNull(refererUrl) || referer.indexOf("://localhost")>-1 || referer.indexOf("://127.0.0.1")>-1){
            return 1;
        }

        String[] strsUrls = refererUrl.split(",");
        logger.info("refererUrl="+strsUrls);
        for (int i = 0; i < strsUrls.length; i++) {
            if (referer.startsWith(strsUrls[i])) {
                return 1;
            }
        }
        logger.info("当前请求来源地址为：" + referer + "，不是合法请求地址来源；安全地址为："+strsUrls);
        return 0;
    }


    private String getIpAddress(HttpServletRequest request, boolean isAll) {
        if(isAll){
            String serverId = PassportHelper.getInstance().getCurrCookie(request,"__guuid");
            logger.info("__guuid="+serverId);
            if(StrUtil.isNotNull(serverId)){//以serverID为主
                return serverId;
            }else{
                return request.getRequestedSessionId();
            }
        }
        String ip = FilterConfiguration.getIpAddress(request);
        return ip;
    }

    /**
     * 设置500拦截状态
     */
    private void setUnauthorizedResponse(RequestContext requestContext) {
        JSONObject obj = new JSONObject();
        obj.put("errorCode","500");
        obj.put("errorMesage","请求参数非法");
        requestContext.setResponseBody(obj.toJSONString());
        throw new BusinessException("请求参数非法");
    }
}
